#include <stdio.h>
#include "processor.h"
#include "log_elem.h"

guint proc_hash_func (gconstpointer key) {
    guint hash = 0;
    const ProcNode * p = key;
    hash = g_str_hash (p->host) + g_str_hash (p->name) 
                 + (p->pid);
    return hash;
}

gboolean proc_equal_func (gconstpointer a, gconstpointer b) {
    const ProcNode *p1, *p2;
    p1 = a;
    p2 = b;
   if (!g_strcmp0 (p1->host, p2->host) && !g_strcmp0 (p1->name, p2->name)
        && p1->pid == p2->pid)
        return TRUE;
    else
        return FALSE;
}

guint port_hash_func (gconstpointer key) {
    guint hash = 0;
    const PortNode *p = key;
    hash = g_str_hash (p->host) + p->port;
    return hash;
}

gboolean port_equal_func (gconstpointer a, gconstpointer b) {
    const PortNode *p1, *p2;
    p1 = a;
    p2 = b;
    if (!g_strcmp0 (p1->host, p2->host) && p1->port == p2->port) {
        return TRUE;
    } else {
        return FALSE;
    }
}

guint file_hash_func (gconstpointer key) {
    guint hash = 0;
    const FileNode *f;
    f = key;
    hash = g_str_hash (f->host) + g_str_hash (f->name);
    return hash;
}

gboolean file_equal_func (gconstpointer a, gconstpointer b) {
    const FileNode *f1, *f2;
    f1 = a;
    f2 = b;
    if (!g_strcmp0 (f1->host, f2->host) && !g_strcmp0 (f1->name, f2->name)) {
        return TRUE;
    } else {
        return FALSE;
    }
}

guint res_hash_func (gconstpointer key) {
    guint hash = 0;
    const ResNode *r;
    r = key;
    hash = g_str_hash (r->host) + g_str_hash (r->name);
    return hash;
}

gboolean res_equal_func (gconstpointer a, gconstpointer b) {
    const ResNode *r1, *r2;
    r1 = a;
    r2 = b;
    if (!g_strcmp0 (r1->host, r2->host) && !g_strcmp0 (r1->name, r2->name)) {
        return TRUE;
    } else {
        return FALSE;
    }
}


int
processor_init (Processor **p) {
    (*p) = g_new0 (Processor, 1);
    if (*p == NULL)
        return -1;
    (*p)->node_alloc = g_ptr_array_new ();
    (*p)->uid_no = 0;
    (*p)->file_record = g_list_alloc ();
    (*p)->res_record = g_list_alloc ();
    (*p)->proc = g_hash_table_new (proc_hash_func, proc_equal_func);
    (*p)->port = g_hash_table_new (port_hash_func, port_equal_func);
    (*p)->file = g_hash_table_new (file_hash_func, file_equal_func);
    (*p)->res = g_hash_table_new (res_hash_func, res_equal_func);
    return 0;
}

int processor_destruct (Processor *p) {

}

// return uid;
guint processor_add_node (Processor *p, void * node) {
    g_ptr_array_add (p->node_alloc, node);
    p->uid_no++;
    return p->uid_no-1;
}

// Proc->Proc
int
processor_add_terminate_relation (ProcNode *from, ProcNode *to) {
    to->terminated = TRUE;
    return 0;
}

// Proc->Proc
int
processor_add_fork_relation (ProcNode *parent, ProcNode *child) {
    if (!g_ptr_array_find (parent->childs, child, NULL))
        g_ptr_array_add (parent->childs, child);
    child->parent = parent;
    return 0;
}

// File->Proc
int
processor_add_exec_relation (FileNode *file, ProcNode *proc) {
    if (!g_ptr_array_find (file->proc_to, proc, NULL))
        g_ptr_array_add (file->proc_to, proc);
    if (!g_ptr_array_find (proc->file_from, file, NULL))
        g_ptr_array_add (proc->file_from, file);
    return 0;
}

// File->Proc
int
processor_add_read_relation (FileNode *file, ProcNode *proc) {
    if (!g_ptr_array_find (file->proc_to, proc, NULL))
        g_ptr_array_add (file->proc_to, proc);
    if (!g_ptr_array_find (proc->file_from, file, NULL))
        g_ptr_array_add (proc->file_from, file);
    return 0;
}

// Proc->File
int
processor_add_write_relation (ProcNode *proc, FileNode *file) {
    if (!g_ptr_array_find (proc->file_to, file, NULL))
        g_ptr_array_add (proc->file_to, file);
    if (!g_ptr_array_find (file->proc_from, proc, NULL))
        g_ptr_array_add (file->proc_from, proc);
    return 0;
}

int
processor_add_refer_relation (PortNode *port, ProcNode *proc) {
    port->ref = proc;
    return 0;
}

// Proc->Port
int
processor_add_send_relation (ProcNode *proc, PortNode *port) {
    if (!g_ptr_array_find (proc->port_to, port, NULL))
        g_ptr_array_add (proc->port_to, port);
    if (!g_ptr_array_find (port->proc_from, proc, NULL))
        g_ptr_array_add (port->proc_from, proc);
    return 0;
}

// port->Proc
int
processor_add_recv_relation (PortNode *port, ProcNode *proc) {
    if (!g_ptr_array_find (port->proc_to,  proc, NULL))
        g_ptr_array_add (port->proc_to, proc);
    if (!g_ptr_array_find (proc->port_from, port, NULL))
        g_ptr_array_add (proc->port_from, port);
    return 0;
}

// Proc->Proc
int
processor_add_ipc_relation (ProcNode *from, ProcNode *to) {
    if (!g_ptr_array_find (from->ipc_to, to, NULL))
        g_ptr_array_add (from->ipc_to, to);
    if (!g_ptr_array_find (to->ipc_from, from, NULL))
        g_ptr_array_add (to->ipc_from, from);
    return 0;
}

// Proc->Res
int
processor_add_modify_relation (ProcNode *proc, ResNode *res) {
    if (!g_ptr_array_find (proc->res_to, res, NULL))
        g_ptr_array_add (proc->res_to, res);
    if (!g_ptr_array_find (res->proc_from, res, NULL));
        g_ptr_array_add (res->proc_from, res);
    return 0;
}

// Res->Proc
int
processor_add_lookup_relation (ResNode *res, ProcNode *proc) {
    if (!g_ptr_array_find (res->proc_to, proc, NULL))
        g_ptr_array_add (res->proc_to, proc);
    if (!g_ptr_array_find (proc->res_from, res, NULL))
        g_ptr_array_add (proc->res_from, res);
    return 0;
}


int
processor_add_proc_node (Processor *p, const gchar* host, const gchar *name, guint pid) {
    ProcNode *proc;
    guint uid;
    ProcNode tmp_proc;
    tmp_proc.host = host;
    tmp_proc.name = name;
    tmp_proc.pid = pid;
    if (g_hash_table_lookup (p->proc, &tmp_proc) != NULL)
        goto out;
    proc = g_new0 (ProcNode, 1);
    if (proc == NULL) {
        printf ("Insufficient memory.\n");
        return -1;
    }
    proc->host = g_strdup (host);
    proc->name = g_strdup (name);
    proc->pid = pid;
    proc->terminated = FALSE;
    proc->childs = g_ptr_array_new ();
    proc->ipc_from = g_ptr_array_new();
    proc->ipc_to = g_ptr_array_new();
    proc->file_from = g_ptr_array_new();
    proc->file_to = g_ptr_array_new();
    proc->port_from = g_ptr_array_new();
    proc->port_to = g_ptr_array_new();
    proc->res_from = g_ptr_array_new();
    proc->res_to = g_ptr_array_new();
    uid = processor_add_node (p, proc);
    proc->uid = uid;
    g_hash_table_insert (p->proc, proc, proc);
    out:
    return 0;
}

int
processor_add_port_node (Processor *p, const gchar *host, const guint port) {
    PortNode *po;
    guint uid;
    PortNode tmp_po;
    tmp_po.host = host;
    tmp_po.port = port;
    if (g_hash_table_lookup (p->port, &tmp_po) != NULL)
        goto out;
    po = g_new0 (PortNode, 1);
    if (po == NULL) {
        printf ("Insufficient memory.\n");
        return -1;
    }
    po->host = g_strdup (host);
    po->port = port;
    po->proc_from = g_ptr_array_new();
    po->proc_to = g_ptr_array_new();
    uid = processor_add_node (p, port);
    po->uid = uid;
    g_hash_table_insert (p->port, po, po);
    out:
    return 0;
}

int
processor_add_file_node (Processor *p, const gchar *host, const gchar *name) {
    FileNode *f;
    guint uid;
    FileNode tmp_f;
    tmp_f.name = name;
    tmp_f.host = host;
    if (g_hash_table_lookup (p->file, &tmp_f) != NULL)
        goto out;
    f = g_new0 (FileNode, 1);
    if (f == NULL) {
        printf ("Insufficient memory.\n");
        return -1;
    }
    f->host = g_strdup (host);
    f->name = g_strdup (name);
    f->proc_from = g_ptr_array_new();
    f->proc_to = g_ptr_array_new();
    uid = processor_add_node (p, f);
    f->uid = uid;
    g_hash_table_insert (p->file, f, f);
    out:
    return 0;
}

int
processor_add_res_node (Processor *p, const gchar *host, const gchar *name) {
    ResNode *r;
    guint uid;
    FileNode tmp_r;
    tmp_r.name = name;
    tmp_r.host = host;
    if (g_hash_table_lookup (p->file, &tmp_r) != NULL)
        goto out;
    r = g_new0 (ResNode, 1);
    if (r == NULL) {
        printf ("Insufficient memory.\n");
        return -1;
    }
    r->host = g_strdup (host);
    r->name = g_strdup (name);
    r->proc_from = g_ptr_array_new();
    r->proc_to = g_ptr_array_new();
    uid = processor_add_node (p, r);
    r->uid = uid;
    g_hash_table_insert (p->file, r, r);
    out:
    return 0;
}



ProcNode *
processor_proc_lookup (Processor *p, const gchar* host, const gchar *name, guint pid) {
    ProcNode *proc;
    ProcNode tmp_proc;
    tmp_proc.host = host;
    tmp_proc.name = name;
    tmp_proc.pid = pid;
    proc = g_hash_table_lookup (p->proc, &tmp_proc);
    return proc;
}

PortNode *
processor_port_lookup (Processor *p, const gchar *host, const guint port) {
    PortNode *po;
    PortNode tmp_po;
    tmp_po.host = host;
    tmp_po.port = port;
    po = g_hash_table_lookup (p->port, &tmp_po);
    return po;
}

FileNode *
processor_file_lookup (Processor *p, const gchar *host, const gchar *name) {
    FileNode *f;
    FileNode tmp_f;
    tmp_f.name = name;
    tmp_f.host = host;
    f = g_hash_table_lookup (p->file, &tmp_f);
    return f;
}

ResNode *
processor_res_lookup (Processor *p, const gchar *host, const gchar *name) {
    ResNode *r;
    FileNode tmp_r;
    tmp_r.name = name;
    tmp_r.host = host;
    r = g_hash_table_lookup (p->file, &tmp_r);
    return r;
}

int
processor_process (Processor *p, LogElem *el) {
    FileNode *f;
    ProcNode *proc;
    ResNode *r;
    PortNode *port;
    /* extra[0]: host */
    switch (el->type) {
         /* 文件操作 */
        case LE_READ: {
            /*extra[0]:host, extra[1]:image*/
            f = processor_file_lookup (p, el->extra->pdata[0], el->src);
            proc = processor_proc_lookup (p, el->extra->pdata[0],el->extra->pdata[1], atoi(el->dest));
            if (proc!=NULL && f != NULL)
                processor_add_read_relation (f, proc);
            break;
        }
        case LE_WRITE:
            proc = processor_proc_lookup (p, el->extra->pdata[0],el->extra->pdata[1], atoi(el->src));
            f = processor_file_lookup (p, el->extra->pdata[0], el->src);
            if (f == NULL) {
                processor_add_file_node (p, el->extra->pdata[0], el->dest);
            }
            if (proc!=NULL && f != NULL)
                processor_add_read_relation (f, proc);
            break;
        case LE_DELETE:
            break;
        case LE_CREATE:
            break;
        /* 网络操作 */
        case LE_SEND:
            break;
        case LE_RECV:
            break;
        /* 进程操作 */
        case LE_FORK:
            break;
        case LE_EXEC:
            break;
        case LE_IPC:
            break;
        /* 鉴权操作 */
        case LE_LOGIN:
            break;
        case LE_INIT:
            break;
        /* 关联操作 */
        case LE_REFER:
            break;
    }
}